
#include <BImage>
#include "member_BImage.h"
#include "member_BTexture.h"

using namespace BWE;

#define member					(*(member_BImage*)_ptr)
#define member_allocate()		_ptr = new member_BImage(this)
#define member_release()		delete (member_BImage*)_ptr

BImage::BImage()
{
	member_allocate();
}
BImage::BImage(Format format)
{
	member_allocate();
	setFormat(format);
}
BImage::BImage(const BSize& size, Format format)
{
	member_allocate();
	setFormat(format);
	setSize(size);
}
BImage::BImage(int width, int height, Format format)
{
	member_allocate();
	setFormat(format);
	setSize(width, height);
}
BImage::BImage(const BImage& other)
{
	member_allocate();
	setFormat(other.format());
	setSize(other.size());
	setPixels(other.pixels());
	setBlend(other.blend());
	setFilter(other.filter());
	setWrap(other.wrap());
}
BImage::~BImage()
{
	member_release();
}

void BImage::setFormat(Format format)
{
	if (member.format != format)
	{
		member.format = format;
		switch (format)
		{
		case Format_RGB:
			member.formatLen = 3;
			break;
		case Format_RGBA:
			member.formatLen = 4;
			break;
		case Format_Alpha:
			member.formatLen = 1;
			break;
		case Format_Gray:
			member.formatLen = 1;
			break;
		case Format_Gray_Alpha:
			member.formatLen = 2;
			break;
		case Format_Char:
			member.formatLen = 1;
			break;
		case Format_Short:
			member.formatLen = 2;
			break;
		case Format_UChar:
			member.formatLen = 1;
			break;
		case Format_UShort:
			member.formatLen = 2;
			break;
		default:
			member.format = Format_None;
			break;
		}
		this->dirty();
	}
}
Format BImage::format() const
{
	return member.format;
}

void BImage::setSize(const BSize& size)
{
	if(member.size != size)
	{
		member.size = size;
		int count = size.width() * size.height();
		if (count > 0)
		{
			int size = 0;
			switch (member.format)
			{
			case Format_Alpha:
			case Format_Gray:
			case Format_UChar:
				size = count;
				break;
			case Format_RGB:
			case Format_BGR:
				size = count * 3;
				break;
			case Format_RGBA:
			case Format_BGRA:
			case Format_Int:
			case Format_UInt:
				size = count * 4;
				break;
			case Format_Short:
			case Format_UShort:
			case Format_Gray_Alpha:
				size = count * 2;
				break;
			default:
				size = 0;
				break;
			}
			BByteArray* pixels = new BByteArray(size);
			this->setPixels(pixels);
			this->dirty();
		}
	}
}
void BImage::setSize(int width, int height)
{
	setSize(BSize(width, height));
}
const BSize& BImage::size() const
{
	return member.size;
}

void BImage::setWidth(int width)
{
	BSize size(width, member.size.height());
	setSize(size);
}
int BImage::width() const
{
	return member.size.width();
}

void BImage::setHeight(int height)
{
	BSize size(member.size.width(), height);
	setSize(size);
}
int BImage::height() const
{
	return member.size.height();
}

void BImage::setPixels(BByteArray* pixels)
{
	if (member.pixels != pixels)
	{
		member.pixels = pixels;
		this->dirty();
	}
}
void BImage::setPixels(const BByteArray* pixels)
{
	if (member.pixels->size() == pixels->size())
	{
		(*member.pixels) = *pixels;
		this->dirty();
	}
}
BByteArray* BImage::pixels()
{
	return member.pixels;
}
const BByteArray* BImage::pixels() const
{
	return member.pixels;
}

void BImage::clear()
{
	member.format = Format_None;
	member.size.set(0, 0);
	member.pixels = 0;
	this->dirty();
}
bool BImage::empty() const
{
	if(member.size.width() * member.size.height() <= 0)
		return true;
	if(member.format == Format_None)
		return true;
	if(member.pixels == 0)
		return true;
	return false;
}
void BImage::dirty()
{
	member.dirty = true;
	emit(Signal_Dirty);
}

bool BImage::bind() const
{
	BTexture::bind();

	if (texture_member(this)->tbo == 0)
		return false;

	if (!member.pixels || member.pixels->empty())
		return false;

	int w = member.size.width();
	int h = member.size.height();

	if (member.pixels->size() != w * h * member.formatLen)
		return false;

	GLuint tbo = texture_member(this)->tbo;

	if (member.dirty)
	{
		GLvoid* data = (GLvoid*)member.pixels->data();

		glBindTexture(GL_TEXTURE_2D, tbo);
		switch (member.format)
		{
		case Format_Alpha:
		case Format_UChar:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
			break;
		case Format_Short:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_SHORT, data);
			break;
		case Format_UShort:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_SHORT, data);
			break;
		case Format_Int:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_INT, data);
			break;
		case Format_UInt:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_INT, data);
			break;
		case Format_RGBA:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
			break;
		case Format_RGB:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
			break;
		case Format_BGRA:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
			break;
		case Format_BGR:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_BGR, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
			break;
		case Format_Gray:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
			break;
		case Format_Gray_Alpha:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
			break;
		}
		member.dirty = false;
	}
	glBindTexture(GL_TEXTURE_2D, tbo);
	return true;
}

